#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdbool.h>
#include <poll.h>
#include <signal.h>

#define LISTEN_PORT 8888       /* 监听端口号 */
#define INFTIM -1              /* 系统底层的-1宏定义 */
#define RECV_BUF_SIZE 1024     /* 接受缓冲区大小 */
#define SEND_BUF_SIZE 1024     /* 发送缓冲区大小 */
#define LISTENQ 4              /* 最大监听数 */
#define EXIT_STRING_CMD "exit" /* 退出指令 */

void *thread_recv_for_client(void *argv)
{
    struct pollfd client_pollfd;
    int poll_listen_count;
    int read_recv_buf_count;
    char recv_buf[RECV_BUF_SIZE + 1] = {0};

    /* init */
    client_pollfd.fd = *((int *)argv);
    client_pollfd.events = POLLIN;

    // poll listen
    while (true)
    {
        poll_listen_count = poll(&client_pollfd, 1, INFTIM); /* Can't timeout. */
        if (poll_listen_count == -1 ||
            poll_listen_count == 0)
        {
            perror("poll() error: ");
            exit(EXIT_FAILURE);
        }
        if (client_pollfd.revents != POLLIN) // 如果监听到的不是可读事件，则退出
        {
            printf("other error.\n");
            exit(EXIT_FAILURE);
        }

        // listened
        printf("client: ");
        read_recv_buf_count = read(client_pollfd.fd, recv_buf, RECV_BUF_SIZE);
        if (read_recv_buf_count == -1)
        {
            perror("read() error: ");
            exit(EXIT_FAILURE);
        }
        // 如果收到退出指令字符串就退出
        if (strstr(recv_buf, EXIT_STRING_CMD) != NULL)
        {
            printf("chat to close.\n");
            exit(EXIT_SUCCESS);
        }
        printf("%s", recv_buf);
        bzero(recv_buf, RECV_BUF_SIZE);
    }
}

int main(int argc, char *argv[])
{
    struct sockaddr_in serveraddr, clientaddr;
    socklen_t clientlen;
    int listenfd, clientfd;
    struct pollfd server_pollfd;
    int poll_listen_count;
    pthread_t pthread_recv_tid;
    char send_buf[SEND_BUF_SIZE + 1] = {0};

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1)
    {
        perror("new socket error: ");
        exit(EXIT_FAILURE);
    }

    /* init server */
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(LISTEN_PORT);

    /* binding */
    bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

    /* listening */
    listen(listenfd, LISTENQ);
    printf("start listen.\n");

    server_pollfd.fd = listenfd;
    server_pollfd.events = POLLRDNORM;

    /* 等待接受客户端 */
    printf("wait connection for client......\n");
    poll_listen_count = poll(&server_pollfd, 1, INFTIM);
    if (poll_listen_count == -1 ||
        poll_listen_count == 0)
    {
        perror("poll() error: ");
        exit(EXIT_FAILURE);
    }
    if (server_pollfd.revents == POLLRDNORM)
    {
        clientlen = sizeof(clientaddr);
        clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen);
        if (clientfd < 0)
        {
            perror("accept() error: ");
            exit(EXIT_FAILURE);
        }
        printf("accept new client: %s:%d\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port);

        // start chat
        printf("start chat:\n");
        pthread_create(&pthread_recv_tid, NULL, thread_recv_for_client, &clientfd);
        while (true)
        {
            if (fgets(send_buf, SEND_BUF_SIZE, stdin) != NULL)
            {
                write(clientfd, send_buf, strlen(send_buf));
                if (strstr(send_buf, EXIT_STRING_CMD) != NULL)
                {
                    printf("chat to close.\n");
                    exit(EXIT_FAILURE);
                }
                bzero(send_buf, SEND_BUF_SIZE);
            }
        }
    }
    return 0;
}